# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import json
import google.protobuf.json_format as json_format
import coin.exchange.base.kr_rest.private_client_base as privcb
import coin.exchange.huobi_futures.kr_rest.native_private_client as nprivc
from coin.exchange.huobi.kr_rest.currency import HuobiCurrency
from coin.exchange.huobi_futures.kr_rest.futures_product import HuobiFuturesProduct
from coin.exchange.huobi_futures.kr_rest.product_info import huobi_futures_product_info
from coin2.exchange.order.system_pb2 import OrderSystemConfig
from coin2.exchange.symbology_pb2 import ProductConfig
from cc.appcoin2.support.order.py_parser import PyOrderParser

from coin.proto.coin_query_pb2 import (AccountBalance,
                                       CurrencyBalance,
                                       AccountPosition,
                                       ProductPosition)


class HuobiFuturesPrivateParser(object):
  @staticmethod
  def parse_balance(update_msg):
    """
    {
      "status": "ok",
      "data": [
        {
          "symbol": "BTC",
          "margin_balance": 0.5004010868571045,
          "margin_position": 0,
          "margin_frozen": 0.0025,
          "margin_available": 0.4979010868571045,
          "profit_real": 0.0,
          "profit_unreal": 0,
          "risk_rate": 199.96043474284178,
          "withdraw_available": 0.4979010868571045,
          "liquidation_price": null,
          "lever_rate": 20
        },
        {
          "symbol": "ETH",
          "margin_balance": 0,
          "margin_position": 0,
          "margin_frozen": 0,
          "margin_available": 0,
          "profit_real": 0,
          "profit_unreal": 0,
          "risk_rate": null,
          "withdraw_available": 0,
          "liquidation_price": null,
          "lever_rate": 20
        }
      ],
      "ts": 1545023541960
    }
    """
    assert update_msg['status'] == 'ok'
    currency_balance_list = []
    for balance_info in update_msg['data']:
      native_currency = balance_info['symbol']
      currency = HuobiCurrency.FromStrNativeCurrencyNothrow(native_currency)
      if currency is None:
        continue

      unrealized_pnl = float(balance_info['profit_unreal'])
      total = float(balance_info['margin_balance'])
      hold = float(balance_info['margin_frozen'])
      available = float(balance_info['margin_available'])
      currency_balance = CurrencyBalance(currency=currency.currency,
                                         currency_native=currency.native_currency,
                                         total=total,
                                         hold=hold,
                                         available=available)
      currency_balance_list.append(currency_balance)

    return AccountBalance(
        exchange='Huobi',
        each_balance=currency_balance_list,
    )

  @staticmethod
  def parse_account_position(update_msg):
    """
    {
      "status": "ok",
      "data": [
        {
          "symbol": "BTC",
          "contract_code": "BTC190329",
          "contract_type": "quarter",
          "volume": 2.0,
          "available": 2.0,
          "frozen": 0.0,
          "cost_open": 3169.284997720306,
          "cost_hold": 3169.284997720306,
          "profit_unreal": 0.0003239832919784,
          "profit_rate": 0.10267953867790408,
          "lever_rate": 20,
          "position_margin": 0.003139086651347923,
          "direction": "buy",
          "profit": 0.0003239832919784
        },
        {
          "symbol": "BTC",
          "contract_code": "BTC190329",
          "contract_type": "quarter",
          "volume": 1.0,
          "available": 1.0,
          "frozen": 0.0,
          "cost_open": 3170.02,
          "cost_hold": 3170.02,
          "profit_unreal": -0.0001546757859384,
          "profit_rate": -0.09806506698810896,
          "lever_rate": 20,
          "position_margin": 0.001569543325673961,
          "direction": "sell",
          "profit": -0.0001546757859384
        }
      ],
      "ts": 1545034974782
    }
    """
    assert update_msg['status'] == 'ok', update_msg
    product_positions = {}
    for pos_info in update_msg['data']:
      native_symbol = pos_info['contract_code']
      product = HuobiFuturesProduct.FromStrNativeProduct(native_symbol)
      position = product_positions.get(native_symbol)
      if position is None:
        position = ProductPosition(symbol=product.symbol)
        product_positions[native_symbol] = position
      if pos_info['direction'] == 'buy':
        position.long_position = float(pos_info['volume'])
        position.available_long_position = float(pos_info['available'])
      elif pos_info['direction'] == 'sell':
        position.short_position = -float(pos_info['volume'])
        position.available_short_position = -float(pos_info['available'])
      else:
        raise ValueError('Invalid direction!')

    for position in product_positions.values():
      position.net_position = position.long_position + position.short_position

    return AccountPosition(exchange='Huobi', each_position=product_positions.values())


class HuobiFuturesPrivateClient(privcb.PrivateClientBase):
  ProductType = HuobiFuturesProduct

  def __init__(self, key_file):
    privcb.PrivateClientBase.__init__(self)
    self.nprivc = nprivc.HuobiFuturesNativePrivateClient(key_file=key_file)
    pi_bundle = huobi_futures_product_info._product_infos
    assert pi_bundle.HasField('mea')
    oss_config = OrderSystemConfig(
      mea=pi_bundle.mea,
      products=ProductConfig(norms=[pi.symbol for pi in pi_bundle.product_infos]))
    self._parser = PyOrderParser.from_mea_str(pi_bundle.mea)
    self._parser.add_product(oss_config.SerializeToString())

  def query_account_balance_impl(self):
    update = self.nprivc.query_account_info()
    msg = self._parser.parse_account_balance(json.dumps(update.msg))
    account_balance = AccountBalance()
    json_format.Parse(msg, account_balance)
    update.msg = account_balance
    return update

  def query_account_position_impl(self):
    update = self.nprivc.query_position_info()
    msg = self._parser.parse_account_position(json.dumps(update.msg))
    account_position = AccountPosition()
    json_format.Parse(msg, account_position)
    update.msg = account_position
    return update

  def query_fills_impl(self, product):
    raise NotImplementedError()

  def query_list_orders_impl(self, product):
    raise NotImplementedError()

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError()

  def query_cancel_product_impl(self, product):
    raise NotImplementedError()
